package com.nowcoder.topic.dp.middle;

/**
 * NC268 矩形覆盖
 * @author d3y1
 */
public class NC268{
    public int rectCover(int target) {
//        return solution1(target);
        return solution2(target);
//        return solution3(target);
//        return solution4(target);
    }

    /**
     * 数学法
     *
     * 举例子找规律(画图) => 2个2*1的小矩形横放看做一个整体(日字形) => 转化为 日字形与小矩形竖放的组合数
     * 可得:
     * F(n) = C(n,0) + C(n-1,1) + C(n-2,2) + ... + C(n-n/2,n/2)
     *
     * @param target
     * @return
     */
    private int solution1(int target){
        if(target == 0){
            return 0;
        }

        int result = 0;

        for(int i=0; i<=target/2; i++){
            result += C(target-i, i);
        }

        return result;
    }

    // C(n,k)
    private long C(int n, int k){
        k = Math.max(k, n-k);

        long up=1,down=1;
        for(int i=1; i<=n-k; i++){
            up *= n-i+1;
            down *= i;
        }

        return up/down;
    }

    /**
     * 动态规划
     *
     * 举例子找规律(画图) => 2个2*1的小矩形横放看做一个整体(日字形)
     * 可得:
     * F(1) = 1
     * F(2) = 2
     * F(3) = 3 = 1 + 2
     * F(4) = 5 = 2 + 3
     * F(5) = 8 = 3 + 5
     * F(6) = 13= 5 + 8
     * ...
     * F(n) = F(n-1) + F(n-2)  , n>=3
     *
     * 结合图形理解F(n):
     * F(n-1)里每个方案最右边竖放一个小矩形
     * F(n-2)里每个方案最右边横放两个小矩形(日字形)
     *
     * dp[i]表示有i个小矩形时的覆盖方法数
     *
     * dp[i] = dp[i-1] + dp[i-2]  , i>=3
     *
     * @param target
     * @return
     */
    private int solution2(int target){
        if(target == 0){
            return 0;
        }
        if(target == 1){
            return 1;
        }

        int[] dp = new int[target+1];
        dp[1] = 1;
        dp[2] = 2;

        for(int i=3; i<=target; i++){
            dp[i] = dp[i-1] + dp[i-2];
        }

        return dp[target];
    }

    /**
     * 动态规划: 空间优化
     * @param target
     * @return
     */
    private int solution3(int target){
        if(target == 0){
            return 0;
        }
        if(target == 1){
            return 1;
        }
        if(target == 2){
            return 2;
        }

        int result = 0;
        int head = 1, pre = 2;

        for(int i=3; i<=target; i++){
            result = pre + head;
            head = pre;
            pre = result;
        }

        return result;
    }

    /**
     * 递归
     * @param target
     * @return
     */
    private int solution4(int target){
        if(target == 0){
            return 0;
        }

        return F(target);
    }

    /**
     * F(n)
     * @param n
     * @return
     */
    private int F(int n){
        if(n == 1){
            return 1;
        }
        if(n == 2){
            return 2;
        }

        return F(n-1)+F(n-2);
    }
}